﻿using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace AutoRegister
{
    internal class DefaultAssemblyProvider : IAssemblyProvider
    {
        private readonly string directory;
        private readonly IEnumerable<GlobMatcher> globs;
        private readonly IEnumerable<Assembly> caches;

        public DefaultAssemblyProvider(AutoRegisterOptions options)
        {
            this.directory = AppDomain.CurrentDomain.BaseDirectory;

            this.globs = options.Matches.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => new GlobMatcher(o)).ToList();

            this.ImportAssemblies(options);

            this.LoadAssemblies(options);

            this.caches = this.GetAssemblies(options).Distinct().ToList();
        }

        public IEnumerable<Assembly> GetAssemblies()
        {
            return this.caches;
        }

        private IEnumerable<Assembly> GetAssemblies(AutoRegisterOptions config)
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies()
                .Where(assembly => !assembly.IsDynamic && !assembly.ReflectionOnly)
                .Where(assembly => this.globs.Any(o => o.IsMatch(assembly.GetName().Name)))
                .ToList();

            foreach (var assembly in assemblies)
            {
                yield return assembly;
            }

            if (config.AddEntry)
            {
                yield return Assembly.GetEntryAssembly();
            }
        }

        private void LoadAssemblies(AutoRegisterOptions config)
        {
            var assemblyNames = DependencyContext.Default.GetDefaultAssemblyNames()
                .Where(assemblyName => this.globs.Any(o => o.IsMatch(assemblyName.Name)));

            foreach (var assemblyName in assemblyNames)
            {
                Assembly.Load(assemblyName);
            }
        }

        private void ImportAssemblies(AutoRegisterOptions config)
        {
            foreach (var item in config.Imports)
            {
                if (string.IsNullOrWhiteSpace(item))
                {
                    continue;
                }

                if (Path.IsPathRooted(item))
                {
                    Assembly.LoadFrom(item);
                }
                else
                {
                    Assembly.LoadFrom(Path.Combine(this.directory, item));
                }
            }
        }
    }
}
